package com.open.center.common.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.CollectionUtils;

/**
 * 对象拷贝工具
 *
 * @author Riche's
 * @since 2025/5/23
 */
@Slf4j
public class CopyUtils {

    private CopyUtils() {

    }

    /**
     * 拷贝单个对象
     *
     * @param source 源对象
     * @param clazz  目标对象类
     * @param <T>    目标对象类型
     * @return 目标对象
     */
    public static <T> T copy(Object source, Class<T> clazz) {
        if (source == null) {
            return null;
        }

        T target;
        try {
            target = clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException
                 | IllegalAccessException
                 | NoSuchMethodException
                 | InvocationTargetException e) {
            log.error("{}", e.getMessage());
            return null;
        }
        BeanUtils.copyProperties(source, target);

        return target;
    }

    /**
     * 拷贝对象列表
     *
     * @param source 源对象列表
     * @param clazz  目标对象类型
     * @param <T>    目标对象类型
     * @return 目标对象列表
     */
    public static <T> List<T> listCopy(List<?> source, Class<T> clazz) {
        List<T> targetList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(source)) {
            for (Object s : source) {
                T target = copy(s, clazz);
                targetList.add(target);
            }
        }
        return targetList;
    }


    /**
     * 拷贝对象不为Null的属性
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyNonNull(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
    }

    private static String[] getNullPropertyNames(Object object) {
        final BeanWrapperImpl wrapper = new BeanWrapperImpl(object);
        return Stream.of(wrapper.getPropertyDescriptors())
                .map(PropertyDescriptor::getName)
                .filter(propertyName -> wrapper.getPropertyValue(propertyName) == null)
                .toArray(String[]::new);
    }
}
